feat: vault relay-server (Postgres + /api/vault)#7
Merged
Conversation
atem now generates ids that may contain non-ASCII characters and percent-encodes them into the relay URL. The old sanitizer stripped non-ASCII and never decoded. Now decode first, keep non-ASCII, and restrict ASCII to [A-Za-z0-9-] (matches atem's identity rule). Extracted into sanitize_atem_id() with round-trip tests. 🤖 Built with SMT <smt@agora.build>
Implements the relay-server side of the vault per docs/specs/2026-05-29-vault-relay-server-design.md: - vault_store.rs: VaultStore trait with an InMemoryVaultStore (tests / DB-less runs) and a PgVaultStore (sqlx, runtime queries so no DB is needed at build time). Append/override run in a transaction; current view = highest version per entry_no, history = all rows by seq, ?since filters by seq. - migrations/0001_vault.sql: vaults + vault_entries tables. - AppState gains `vault: Arc<dyn VaultStore>`; main() uses Postgres when DATABASE_URL is set (runs migrations), else in-memory with a warning. - Session gains astation_id (Option A); resolve_caller binds work_session_id to the session's astation_id (falls back to the verify cache). can_read/can_write enforce in-session vs past-writer. - vault_routes.rs: 5 endpoints (create/list/read/write/set-summary) with 401/403/404 handling. 6 oneshot handler tests + 4 store tests. - docker-compose: postgres service + DATABASE_URL; README documents the endpoints and env var. 🤖 Built with SMT <smt@agora.build>
Store: multiple appends increment entry_no/seq, override isolation per entry_no, override of unknown entry starts at v1, not-found on every op, cross-vault isolation, set_summary reflected in list, vault_id format. Routes: 404 on missing vault, 400 on missing ?id, 401 on ungranted session, list isolation by work session, incrementing entry_no over HTTP. 171 tests pass (was 159). 🤖 Built with SMT <smt@agora.build>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements the relay-server side of the vault per
docs/specs/2026-05-29-vault-relay-server-design.md— a durable, append-only, versioned shared context store that collaborating atems read/write. This is the first persistent store in the relay-server (previously fully in-memory).What's new
vault_store.rs—VaultStoretrait with two impls:InMemoryVaultStore(tests + DB-less runs)PgVaultStore(sqlx, runtime queries so no live DB is needed at build time)(vault_id, entry_no, version)collisions). Current view = highest version perentry_no; history = all rows byseq;?sincefilters byseq.migrations/0001_vault.sql—vaults+vault_entriestables; run automatically at startup viasqlx::migrate!.AppState.vault: Arc<dyn VaultStore>— main() uses Postgres whenDATABASE_URLis set, else in-memory with a warning (keeps the server runnable without a DB for its other features).Sessiongainsastation_id;resolve_callerbindswork_session_idto it (falls back to the verify cache).can_read/can_writeenforce in-session vs. past-writer.vault_routes.rs— 5 endpoints (POST/GET /api/vault,GET/POST /api/vault/:id,POST /api/vault/:id/summary) with 401/403/404 handling.[A-Za-z0-9-].postgresservice +DATABASE_URL; README documents endpoints + env var.Wire contract (matches the shipped atem CLI)
Test plan
cargo build+cargo build --releasesucceed (no DB needed at build time)since, writer_list dedup, list authz, multi-append increments, override isolation per entry_no, override-unknown→v1, not-found on every op, cross-vault isolation, summary-in-list, vault_id formatsince, authz (in-session r/w, out-of-session past-writer read-only → 403 write, stranger → 403 read), 401 missing/ungranted session, 400 missing?id, 404 missing vault, list isolation by work session, incrementing entry_no over HTTP, set-summaryDATABASE_URL,docker compose up) — acceptance test in the spec §Acceptance (the only remaining gap;PgVaultStoreis exercised only at runtime)Design note
The spec's Task 1 suggested fail-fast when
DATABASE_URLis unset. I made it an in-memory fallback + warning instead so the relay's RTC/voice/pairing features still run without Postgres; vault durability requiresDATABASE_URL. Easy to flip to hard-fail if preferred.Generated with SMT smt@agora.build